package org.study.sync;

import com.alibaba.fastjson2.JSON;
import lombok.Data;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.*;

/**
 * https://www.freesion.com/article/76011072280/
 *
 * @author Administrator
 * @date 2021-08-30
 */
public class CompletableFuture2 {
    public static void main(String[] args) {
        ArticleService articleService = new ArticleService();
        ItemVo itemVo = articleService.load(1L);
        System.out.println(JSON.toJSONString(itemVo));
    }
}

class ArticleService {
    private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
            2 * Runtime.getRuntime().availableProcessors() + 1,
            5L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),
            new ThreadPoolExecutor.CallerRunsPolicy());

    public ItemVo load(Long articleId) {
        System.out.println("调用 ArticleService.load() 方法开始：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        ItemVo itemVo = new ItemVo();
        ArticleClient articleClient = new ArticleClient();
        UserClient userClient = new UserClient();

        // 1. 查询文章详情 0.5s
        // 下面的查询需要用到文章对应的发布用户，所以这里需要使用CompletableFuture.supplyAsync
        CompletableFuture<ArticleEntity> articleCompletableFuture = CompletableFuture.supplyAsync(() -> {
            ResponseVo<ArticleEntity> skuEntityResponseVo = articleClient.getArticleById(articleId);
            ArticleEntity articleEntity = skuEntityResponseVo.getData();
            if (articleEntity == null) {
                return null;
            }
            itemVo.setId(articleId);
            itemVo.setTitle(articleEntity.getTitle());
            itemVo.setDefaultImage(articleEntity.getDefaultImage());
            return articleEntity;
        }/*, threadPoolExecutor*/);

        // 2. 查询文章博主个人信息 0.5s
        // 这里查询需要依赖文章关联的用户id，所以需要使用articleCompletableFuture.thenAcceptAsync()
        CompletableFuture<Void> userCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {
            ResponseVo<UserEntity> categoryResponseVo = userClient.queryUserInfoById(articleEntity.getUserId());
            UserEntity userEntity = categoryResponseVo.getData();
            itemVo.setUserEntity(userEntity);
        }/*, threadPoolExecutor*/);

        // 3. 查询博主相关文章分类 1s
        // 这里查询需要依赖文章关联的用户id，所以需要使用articleCompletableFuture.thenAcceptAsync()
        CompletableFuture<Void> userOtherArticleCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {
            ResponseVo<List<ArticleEntity>> categoryResponseVo = articleClient.queryArticleListByCategoryId(articleEntity.getUserId());
            List<ArticleEntity> articleEntityList = categoryResponseVo.getData();
            itemVo.setSameCategoryArticleList(articleEntityList);
        }/*, threadPoolExecutor*/);

        // 4. 查询文章评论 1s
        // 不需要依赖其他请求返回值，可以使用新的异步对象 CompletableFuture.runAsync()
        CompletableFuture<Void> commentsCompletableFuture = CompletableFuture.runAsync(() -> {
            ResponseVo<List<CommentEntity>> listResponseVo = userClient.queryCommentListByArticleId(articleId);
            List<CommentEntity> commentEntityList = listResponseVo.getData();
            itemVo.setCommentList(commentEntityList);
        }/*, threadPoolExecutor*/);

        // 5. 相关推荐文章 1s
        // 不需要依赖其他请求返回值，可以使用新的异步对象 CompletableFuture.runAsync()
        CompletableFuture<Void> relatedArticlesCompletableFuture = CompletableFuture.runAsync(() -> {
            ResponseVo<List<ArticleEntity>> userArticleCategoryVo = articleClient.queryRelatedArticleList(articleId);
            List<ArticleEntity> articleEntityList = userArticleCategoryVo.getData();
            itemVo.setRelatedArticleList(articleEntityList);
        }/*, threadPoolExecutor*/);

        // 多任务执行组合 CompletableFuture.allOf()
        CompletableFuture.allOf(articleCompletableFuture, userCompletableFuture, userOtherArticleCompletableFuture, commentsCompletableFuture, relatedArticlesCompletableFuture).join();
        System.out.println("调用 ArticleService.load() 方法结束：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        return itemVo;
    }
}

class ArticleClient {
    ResponseVo<ArticleEntity> getArticleById(Long id) {
        System.out.println("调用 ArticleClient.getArticleById() 方法开始：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        ResponseVo<ArticleEntity> responseVo = new ResponseVo<>();
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ArticleEntity articleEntity = new ArticleEntity();
        articleEntity.setUserId(1L);
        articleEntity.setTitle("接口测试");
        articleEntity.setDefaultImage("http://www.baidu.com/12345.png");

        responseVo.setData(articleEntity);
        System.out.println("调用 ArticleClient.getArticleById() 方法结束：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        return responseVo;
    }

    ResponseVo<List<ArticleEntity>> queryArticleListByCategoryId(Long categoryId) {
        System.out.println("调用 ArticleClient.queryArticleListByCategoryId() 方法开始：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        ResponseVo<List<ArticleEntity>> responseVo = new ResponseVo<>();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        responseVo.setData(null);
        System.out.println("调用 ArticleClient.queryArticleListByCategoryId() 方法结束：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        return responseVo;
    }

    ResponseVo<List<ArticleEntity>> queryRelatedArticleList(Long articleId) {
        System.out.println("调用 ArticleClient.queryRelatedArticleList() 方法开始：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        ResponseVo<List<ArticleEntity>> responseVo = new ResponseVo<>();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        responseVo.setData(null);
        System.out.println("调用 ArticleClient.queryRelatedArticleList() 方法结束：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        return responseVo;
    }
}

class UserClient {
    ResponseVo<UserEntity> queryUserInfoById(Long userId) {
        System.out.println("调用 UserClient.queryUserInfoById() 方法开始：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        ResponseVo<UserEntity> responseVo = new ResponseVo<>();
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        responseVo.setData(null);
        System.out.println("调用 UserClient.queryUserInfoById() 方法结束：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        return responseVo;
    }

    ResponseVo<List<CommentEntity>> queryCommentListByArticleId(Long articleId) {
        System.out.println("调用 UserClient.queryCommentListByArticleId() 方法开始：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        ResponseVo<List<CommentEntity>> responseVo = new ResponseVo<>();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        responseVo.setData(null);
        System.out.println("调用 UserClient.queryCommentListByArticleId() 方法结束：" + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.now()));
        return responseVo;
    }
}

@Data
class ArticleEntity {
    private Long id;
    private Long userId;
    private Long categoryId;
    private String title;
    private String defaultImage;
}

@Data
class UserEntity {

}

@Data
class CommentEntity {

}

@Data
class ItemVo {
    private Long id;
    private Long userId;
    private Long categoryId;
    private String title;
    private String defaultImage;
    private UserEntity userEntity;
    private List<ArticleEntity> sameCategoryArticleList;
    private List<CommentEntity> commentList;
    private List<ArticleEntity> relatedArticleList;
}

@Data
class ResponseVo<T> {
    private String resCode;
    private LocalDateTime resTime = LocalDateTime.now();
    private String errMsg;
    private String extMsg;
    private T data;
}